import { VEEdge, VEFace, VELoop, VEMesh, VEVertex } from "../core/";

const pos_ = new BABYLON.Vector3();
const nor_ = new BABYLON.Vector3();

// TODO：部分冗余点不能被剔除，因为法线不一致，会直接影响显示效果
export function babylonToVEMesh(ve_mesh: VEMesh, mesh: BABYLON.AbstractMesh,
  tolerance: number = 1e-10): void {
  ve_mesh.clear();

  const positions = mesh.getVerticesData(BABYLON.VertexBuffer.PositionKind);
  const normals = mesh.getVerticesData(BABYLON.VertexBuffer.NormalKind);
  const indexVertexArray = mesh.getIndices();
  let nbOfFaces = indexVertexArray!.length / 3;

  const originUV = mesh.getVerticesData(BABYLON.VertexBuffer.UVKind);
  const originUV2 = mesh.getVerticesData(BABYLON.VertexBuffer.UV2Kind);
  const originUV3 = mesh.getVerticesData(BABYLON.VertexBuffer.UV2Kind);
  const originUV4 = mesh.getVerticesData(BABYLON.VertexBuffer.UV2Kind);
  const originUV5 = mesh.getVerticesData(BABYLON.VertexBuffer.UV2Kind);
  const originUV6 = mesh.getVerticesData(BABYLON.VertexBuffer.UV2Kind);

  // console.warn(originUV);
  // console.warn(originUV2);
  // console.warn(originUV3);
  // console.warn(originUV4);
  // console.warn(originUV5);
  // console.warn(originUV6);

  let getVertexIndex = function (bufferIndex: number) {
    return indexVertexArray![bufferIndex];
  }

  const loopMap = new Map<string, VELoop>();
  const vertexMap = new Map<number, VEVertex>();
  const edgeMap = new Map<string, VEEdge>();

  const loops = new Array<VELoop>(3).fill({} as VELoop);

  // console.error(positions);
  // console.error(normals);
  // console.error(indexVertexArray);

  for (let faceIndex = 0; faceIndex < nbOfFaces; faceIndex++) {

    for (let i = 0; i < 3; i++) {
      // v1, from vertex
      const i1 = getVertexIndex(faceIndex * 3 + i);
      let v1 = vertexMap.get(i1);
      if (!v1) {
        pos_.copyFromFloats(positions![i1 * 3], positions![i1 * 3 + 1], positions![i1 * 3 + 2]);
        nor_.copyFromFloats(normals![i1 * 3], normals![i1 * 3 + 1], normals![i1 * 3 + 2]);
        // pos_.fromBufferAttribute(positions, i1);
        v1 = ve_mesh.addVertex(pos_, nor_, true);
        vertexMap.set(i1, v1);
      }

      // v2, to vertex
      const i2 = getVertexIndex(faceIndex * 3 + (i + 1) % 3);
      let v2 = vertexMap.get(i2);
      if (!v2) {
        pos_.copyFromFloats(positions![i2 * 3], positions![i2 * 3 + 1], positions![i2 * 3 + 2]);
        nor_.copyFromFloats(normals![i2 * 3], normals![i2 * 3 + 1], normals![i2 * 3 + 2]);
        // pos_.fromBufferAttribute(positions, i2);
        v2 = ve_mesh.addVertex(pos_, nor_, true);
        vertexMap.set(i2, v2);
      }

      // create edge & link edge，保持边唯一性
      const vertex_hash1 = v1.index + '-' + v2.index;
      const vertex_hash2 = v2.index + '-' + v1.index;
      let edge1 = edgeMap.get(vertex_hash1);
      if (!edge1) {
        edge1 = ve_mesh.addEdge(v1, v2);
        // console.log('edge');
        // console.warn(edge1);
        edgeMap.set(vertex_hash1, edge1);
        edgeMap.set(vertex_hash2, edge1);
      } else {

      }
      v2.edge = edge1;

      // create loop
      let loop1 = loopMap.get(vertex_hash1);
      if (!loop1) {
        loop1 = ve_mesh.addLoop(v1, v2, edge1);
        const loop2 = loop1.pair;
        // const hash2 = i2 + '-' + i1;
        loopMap.set(vertex_hash1, loop1);
        loopMap.set(vertex_hash2, loop2);
      }

      loops[i] = loop1;

      // uv1 data
      if (originUV && originUV.length > 0) {
        loop1.uv[0] = originUV[i1 * 2];
        loop1.uv[1] = originUV[i1 * 2 + 1];
      }
    }
    ve_mesh.addFace(loops);

  }

}


export function VEMeshToBabylon(ve_mesh: VEMesh, mesh: BABYLON.Mesh,
  tolerance: number = 1e-10): void {
  const positions: number[] = [];
  const indices: number[] = [];
  const uvs: number[] = [];

  let index_counter: number = ve_mesh.vertices.length - 1;

  console.log('起始：' + ve_mesh.vertices.length);

  for (let i = 0; i < ve_mesh.vertices.length; i++) {
    let vertex: VEVertex = ve_mesh.vertices[i];
    positions.push(vertex.position.x);
    positions.push(vertex.position.y);
    positions.push(vertex.position.z);
    uvs.push(0, 0);
  }

  const vertexToUVMap = new Map<number, number[][]>();
  const vertexToIndexMap = new Map<number, number[]>();

  for (let i = 0; i < ve_mesh.faces.length; i++) {
    let face: VEFace = ve_mesh.faces[i];

    const first_loop: VELoop = face.first_loop;
    let iter_loop: VELoop = first_loop;
    do {
      let vertex: VEVertex = iter_loop.vertex;
      // 判断是否已添加
      if (vertexToUVMap.has(vertex.index)) {
        let vIndex: number = vertex.index;
        let matches: boolean = false;

        // 若已添加，则判断UV值是否相等，同一顶点可能包含N多组UV
        const uvArray = vertexToUVMap.get(vertex.index);
        for (let j = 0; j < uvArray!.length; j++) {
          if (matchesUV(iter_loop.uv, uvArray![j], tolerance)) {
            matches = true;
            vIndex = (vertexToIndexMap.get(vertex.index))![j];
            break;
          }
        }

        if (matches) {
          // 若相等，indices数据中添加对应vertex索引信息
          indices.push(vIndex);
        } else {
          // 若不相等，添加新的positions信息，添加uvs信息，创建新的索引
          vIndex = ++index_counter;
          positions.push(vertex.position.x);
          positions.push(vertex.position.y);
          positions.push(vertex.position.z);
          uvs.push(...iter_loop.uv);
          indices.push(vIndex);
          (vertexToUVMap.get(vertex.index))!.push(iter_loop.uv);
          (vertexToIndexMap.get(vertex.index))!.push(vIndex);
        }
      } else {
        vertexToUVMap.set(vertex.index, [iter_loop.uv]);
        const v_index: number = ve_mesh.vertices.indexOf(vertex);
        vertexToIndexMap.set(vertex.index, [v_index]);
        indices.push(v_index);
        uvs[v_index * 2] = iter_loop.uv[0];
        uvs[v_index * 2 + 1] = iter_loop.uv[1];
      }

      // 指向下一个loop结构
      iter_loop = iter_loop.next;
    } while (first_loop !== iter_loop)

  }

  var normals: any = [];
  const vertexData = new BABYLON.VertexData();
  BABYLON.VertexData.ComputeNormals(positions, indices, normals);
  vertexData.positions = positions;
  vertexData.indices = indices;
  vertexData.normals = normals;
  vertexData.uvs = uvs;
  // console.warn(uvs);
  vertexData.applyToMesh(mesh);

  // mesh.getScene().removeMesh(mesh);

  // mesh.isVisible = false;
  // mesh.isPickable = false;
  console.log('最终：' + positions.length / 3);

  // let meshObject = {};
  // mesh.serialize(meshObject)
  // console.warn(meshObject);

  // let meshObject = BABYLON.SceneSerializer.SerializeMesh(mesh);
  // console.warn(meshObject);

  // let strScene = JSON.stringify(meshObject);
  // let blob = new Blob([strScene], { type: 'octet/stream' });
  // download(blob, mesh.name + '.babylon');
}


const _v1 = BABYLON.Vector2.Zero();
const _v2 = BABYLON.Vector2.Zero();

function matchesUV(uv1: number[], uv2: number[], tolerance = 1e-10): boolean {
  _v1.copyFromFloats(uv1[0], uv1[1]);
  _v2.copyFromFloats(uv2[0], uv2[1]);
  return _v1.subtract(_v2).length() < tolerance;
}

function download(blob: Blob, fileName: string): void {
  var url = window.URL.createObjectURL(blob);
  var a = document.createElement('a');
  document.body.appendChild(a);
  a.style.display = 'none';
  a.href = url;
  a.download = fileName;
  a.addEventListener('click', () => {
    if (a.parentElement) {
      a.parentElement.removeChild(a);
    }
  });
  a.click();
  window.URL.revokeObjectURL(url);
}